package com.gmall.product.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.util.Arrays;

@Aspect
@Component
public class PerformanceAspect {

    /**
     * JoinPoint  切入点 = 方法
     * PointCut ：方法的集合
     */
//    @Pointcut(value = "@annotation(org.springframework.web.bind.annotation.GetMapping)")
//    public void selectPointCut(){
//
//    }
    // ?   0个或者1个   *  0个或者多个    + 1个或者多个
    //execution(modifiers-pattern?    修饰符匹配（modifier-pattern?）
    //          ret-type-pattern      返回值匹配（ret-type-pattern）：   可以为*表示任何返回值, 全路径的类名等
    //          declaring-type-pattern?   类路径匹配（declaring-type-pattern?）
    //          name-pattern(param-pattern) 方法名匹配（name-pattern）：可以指定方法名 或者 *代表所有, set* 代表以set开头的所有方法
    //          throws-pattern?) 参数匹配（(param-pattern)）：可以指定具体的参数类型，多个参数间用“,”隔开，各个参数也可以用"*" 来表示匹配任意类型的参数，".."表示零个或多个任意参数。
    //如(String)表示匹配一个String参数的方法；(*,String) 表示匹配有两个参数的方法，第一个参数可以是任意类型，而第二个参数是String类型。
    //在包名模式串中，"."表示包下的所有类，而“…**”表示包、子孙包下的所有类。
    @Pointcut(value = "execution(public * com.gmall.product.controller.*Controller.find*(..))")
    public void selectPointCut(){

    }


    /**
     * 前置，后置，环绕，异常，返回
     */
    @Before("selectPointCut()")
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("Before:----------- 前置通知 -----------");
        Signature signature = joinPoint.getSignature();
        System.out.println("Before:返回目标方法的签名：" + signature);
        System.out.println("Before:代理的是哪一个方法：" + signature.getName());
        Object[] args = joinPoint.getArgs();
        System.out.println("Before:获取目标方法的参数信息：" + Arrays.asList(args));
    }

    @Around("selectPointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("Around前:----------- 环绕通知开始 -----------");
        StopWatch stopWatch = new StopWatch();
        try {
            stopWatch.start();
            Object proceed = proceedingJoinPoint.proceed();//执行方法本身
            return proceed;
        } finally {
            stopWatch.stop();
            long totalTimeMillis = stopWatch.getTotalTimeMillis();
            System.out.println("该方法执行的总时长为："+ totalTimeMillis + "ms");
            System.out.println("Around后:---------- 环绕通知结束 -------------");
        }
    }


    @After("selectPointCut()")
    public void afterAdvice(JoinPoint joinPoint){

        System.out.println("After:----------- 后置通知 -----------");
    }

    /**
     * 返回通知
     * 如果参数中的第一个参数为JoinPoint，则第二个参数为返回值的信息
     * 如果参数中的第一个参数不为JoinPoint，则第一个参数为returning中对应的参数
     * returning 只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知，否则不执行
     * @param joinPoint
     * @param keys
     */
    @AfterReturning(value = "selectPointCut()", returning = "keys")
    public void afterReturningAdvice(JoinPoint joinPoint, Object keys){
        System.out.println("----------- 返回通知 -----------");
        System.out.println("AfterReturning:返回通知的返回值：" + keys);
    }

    /**
     * 异常通知
     * 定义一个名字，该名字用于匹配通知实现方法的一个参数名，
     * 当目标方法抛出异常返回后，将把目标方法抛出的异常传给通知方法；
     * throwing 只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知，否则不执行，
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "selectPointCut()", throwing = "e")
    public void afterThrowingAdvice(JoinPoint joinPoint, Exception e){
        System.out.println("AfterThrowing:----------- 异常通知 -----------");
        e.printStackTrace();
    }

}
